#pragma once

#include <gsl/gsl_complex.h>
#include "OpenGLWindow.h"
#include "Environment.h"

struct FFTData;

// Visualizes the results of a natcar simulation run.
class NatcarSimViz : public OpenGLWindow {

public:

	typedef boost::numeric::ublas::vector<double> vec;

	struct Plot {
		int id;
		double SecsPerDiv;	// secs per div
		int HDivs;
		int VDivs;
		int vp[4];			// viewport coordinates
	};

	struct SignalProps {
		std::string Name;	// name of the signal
		vec Rgb;			// color of the signal
		double Scale;		// scaling applied to the signal
		int Plot;			// which plot to display this signal on (0 or 1)
		bool Display;		// should this signal be displayed
	};

	int FieldSizeX;			// the number of points to compute in the magnetic field in the x direction
	int FieldSizeY;			// the number of points to compute in the magnetic field in the y direction
	double FieldZ;			// the field is computed in a plane parallel to the xy plane. This value
							// indicates the z coordinate (height) of the plane
	double FieldScale;		// number to scale the field by for visualization purposes
	double WindowSplit;		// number between (0, 1) representing the fraction of the widow width
							// taken by the left viewport. For example, a value of 0.5 would indicate
							// a 50% split
	double PlaybackSpeed;  // at what rate should the results be played back? 1.0 is real time.

public:
	NatcarSimViz(Environment &env);
	~NatcarSimViz();

	void Run(HINSTANCE hInstance);
	
protected:

	// Initialize opengl stuff
	void InitGL();

	// Called when the window is resized
	void OnResize(GLsizei width, GLsizei height);
	
	// Called before displaying the scene
	void OnUpdate();

	// Called to display the screen
	void OnDisplay();

	// Called whenever a key is pressed
	void OnKeyDown(char c);

private:
	// Draw the simulation visualization viewport (left side)
	void DrawViewport1();

	// Draw the oscilloscope viewport (right side)
	void DrawViewport2();

	// Print relevant GL info to stderr
	void PrintGLInfo();

	// compute the field and store it in a texture
	void InitMagneticField();

	// release all resources associated with the field texture
	void DestroyMagneticField();

	// draw the magnetic field
	void DrawMagneticField();

	// initialize the FFT data
	void InitFFT();

	// destroy the FFT data
	void DestroyFFT();

	// draw all the FFTs
	void DrawFFT();

	// draw the magnitude of a single FFT in the current viewport 
	void DrawFFTMag(const FFTData &fft);

	void DrawPlot(const Plot &p);

	void DrawAxis(const Plot &p);

	// draw a signal in scope view
	void DrawSignal(const void *data, int stride, int length, int index, int numPoints, double scale);

	// the id of the plot
	void DrawSignals(const Plot &p);

private:
	SimResult &mResult;
	Environment &mEnv;
	GLsizei mWindowWidth;
	GLsizei mWindowHeight;
	GLint mVp1[4];
	bool mPause;
	unsigned int mStep;

	bool mDrawField;
	GLuint mFieldTex;

	// data for FFT
	bool mDrawFFT;
	bool mFFTInitialized;
	FFTData *mLsenseDFT;
	FFTData *mRsenseDFT;
	FFTData *mErrorDFT;

	std::map<std::string, int> mSignalMap;	// map of signal names to indices
	std::vector<SignalProps> mSignalProps;	// vector of signal properties
	std::vector<Plot> mPlots;				// vector of plot objects
};
